home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Pascal / source / list manager package ƒ / TextList.p < prev    next >
Encoding:
Text File  |  1990-01-07  |  51.0 KB  |  1,356 lines  |  [TEXT/MPS ]

  1. {
  2.  
  3.                      Sample Source Code by Jack A. Littleton
  4.  
  5.                      Example List Manager Application--TextList
  6.                      ------------------------------------------
  7.  
  8.     TextList.p -- Pascal source code for PTextList
  9.     This file and all files in this package are copyright © 1989 by
  10.     Jack A. Littleton, All rights reserved.
  11.     Version 1.0 - December 3, 1989
  12.     
  13.     READ THE FILE SampleTextList.txt BEFORE CONTINUING!!!!
  14.     
  15.     This package includes:
  16.         TextList.p         -- This file
  17.         TextList.c         -- C source code
  18.         TextList.r         -- Rez source code for all TextList applications
  19.         PTextList.r        -- Pascal-specific Rez source code
  20.         CTextList.r        -- C-specific Rez source code
  21.         PTextList.make     -- Makefile for Pascal TextList application
  22.         CTextList.make     -- Makefile for C TextList application
  23.         
  24.         SampleTextList.txt    -- Documentation for this package
  25.         
  26. ABOUT TextList.p:
  27.     This is the Pascal source code for PTextList, a Pascal implementation of
  28.     an application that uses the List Manager.  This implementation uses the
  29.     only List Definition (LDEF) that is supplied with the system (LDEF #0),
  30.     which can handle only text in the cells.
  31.     
  32.     The application handles multiple windows with a single list in each window.
  33.     The list has both vertical and horizontal scroll bars.  The cell size, font,
  34.     and font size.  A "List"
  35.     menu allows you to set flags that affect how the list reacts to mouse
  36.     clicks (the selFlags field in the ListHandle).  The items that are set in
  37.     the List menu do not take affect until you create a new list.  When a new
  38.     window is created, the application looks at which items in the List menu
  39.     have been selected, and creates the list with those characteristics.  Note
  40.     that changes in the List menu do not affect previously created lists.  This
  41.     means that you can have several different lists open with different
  42.     characteristics, allowing you to see the differences.
  43.     
  44.     The "Edit" menus allows cut/paste, but not through normal scrap techniques.
  45.     Cells can be cut/pasted from one window to another, but not from PTextList
  46.     to another application.  This was implemented to show how to get, set and
  47.     clear data from a cell.
  48.     
  49.     Find is supported in the "File" menu.  This uses the LSearch procedure to
  50.     find a match in the list.
  51.     
  52.     The windows and list support growing and zooming.
  53.     
  54.     Other important notes on implementation are given in the documentation for
  55.     the appropriate routine.
  56.     
  57. A NOTE:  This is just an example of a program that uses the List Manager.  It
  58.     is not meant to be a full-blown application, and may not be suitable as
  59.     a template for a new application.  It is just meant to show as many List
  60.     Manager routines as possible.  In fact, this program was written as
  61.     research for a programming tutorial and was never planned as a application.
  62. }
  63.     
  64. program PTextList;
  65.  
  66. USES MemTypes,                        { memory interface                    }
  67.      QuickDraw,                     { interface to QuickDraw routines    }
  68.      OSIntf,                        { interface to the operating system }
  69.      ToolIntf,                        { interface to the toolbox            }
  70.      PackIntf;                        { interface to package manager        }
  71.  
  72. CONST
  73.     { Menu resource constants }
  74.     NumMenus        = 5;            { The number of menus                 }
  75.     AppleMenu        = 1111;         { Menu Resource Id for apple menu     }
  76.     AppleID         = 1;            { Index ID for apple menu             }
  77.     
  78.     FileMenu        = 1112;         { Menu Resource Id for file menu     }
  79.     FileID            = 2;            { Index ID for file menu             }
  80.     NewItem         = 1;            { Index ID for "New" item            }
  81.     CloseItem        = 2;            { Index ID for "Close" item            }
  82.     AddItem         = 3;            { Index ID for "Add Row" item        }
  83.     DeleteItem        = 4;            { Index ID for "Delete Row" item    }
  84.     FindItem        = 5;            { Index ID for "Find…" item            }
  85.     Quit            = 6;            { Index ID for "Quit" item            }
  86.     
  87.     EditMenu        = 1113;            { Resource ID for "Edit" menu            }
  88.     EditID            = 3;            { Index ID for "Edit" menu                }
  89.     UndoItem        = 1;            { Index ID for "Undo" item--not used    }
  90.     CutItem         = 3;            { Index ID for "Cut" item                }
  91.     CopyItem        = 4;            { Index ID for "Copy" item                }
  92.     PasteItem        = 5;            { Index ID for "Paste" item                }
  93.     ClearItem        = 6;            { Index ID for "Clear" item                }
  94.     SClipItem        = 8;            { Index ID for "Show Clipboard" item    }
  95.     
  96.     ListMenu        = 1114;            { Resource ID for "List" menu                    }
  97.     ListID            = 4;            { Index ID for "List" menu                        }
  98.     OneSelItem        = 1;            { Index ID for "One Selection Only" item         }
  99.     DragWOShItem    = 2;            { Index ID for "Drag Without Shift" item        }
  100.     NoDJointItem    = 3;            { Index ID for "No Disjoint Selections" item    }
  101.     NoExtendItem    = 4;            { Index ID for "Don't Extend" item                }
  102.     NoExpandItem    = 5;            { Index ID for "Don't Expand as Rects" item        }
  103.     UseSenseItem    = 6;            { Index ID for "Use Sense" item                    }
  104.     NoHliteItem     = 7;            { Index ID for "Don't Hilite Empty Cells" item    }
  105.     
  106.     CellMenu        = 1115;            { Resource ID for "Cell" menu        }
  107.     CellID            = 5;            { Index ID for "Cell" menu            }
  108.     IndentItem        = 1;            { Index ID for "Indentation…" item    }
  109.     CSizeItem        = 2;            { Index ID for "Cell Size…" item    }
  110.     FSizeItem        = 4;            { Index ID for "Font Size…" item    }
  111.     
  112.     FontSMenu        = 1;            { Resource ID for "Font" sub-menu    }
  113.     
  114.     { Dialog/Alert box constants }
  115.     cellDlog        = 128;            { Res ID for Cell Size/Indentation dialog    }
  116.     fontSDlog        = 129;            { Res ID for Find dialog                    }
  117.     findDlog        = 130;            { Res ID for Font Size/Cells per Col dialog    }
  118.     aboutDlog         = 1112;            { Res ID for "About…" dialog                }
  119.     
  120.     { String resource constants }
  121.     theStrList        = 128;            { Res ID of string list         }
  122.     indentInd        = 1;            { Index ID for "Indentation"    }
  123.     vertInd            = 2;            { Index ID for "vertical"         }
  124.     horizInd        = 3;            { Index ID for "horizontal"        }
  125.     cSizeInd        = 4;            { Index ID for "Cell Size"        }
  126.     
  127.     theWind            = 1113;
  128.     
  129. TYPE
  130.     MenuListType    = ARRAY [1..numMenus] OF MenuHandle;
  131.     
  132.     DocumentPeek    = ^DocumentRecord;
  133.     DocumentRecord    = RECORD
  134.         docWindow:        WindowRecord;
  135.         docList:        ListHandle;
  136.         END;
  137.     
  138. VAR
  139.     gDragArea:        Rect;                { Area where window can be dragged             }
  140.     gGrowArea:        Rect;                { Minimum size a window can be shrunk to    }
  141.     gActvWindow:     WindowPtr;            { Holds the pointer to the active window     }
  142.     gWindInfo:        DocumentPeek;        { Information about active window            }
  143.     gMenuItem:        MenuListType;        { An array of menu handles                    }
  144.     gEvents:         EventRecord;        { The event record queue                    }
  145.     gFinished:        BOOLEAN;            { Set to true when app finishes                }
  146.     gClipboard:        Str255;                { Private scrap for list                    }
  147.     gClipLen:        INTEGER;            { Length of scrap                            }
  148.     gFontSMenu:        MenuHandle;            { Font submenu handle                        }
  149.     gFontName:        Str255;                { Name of current font                        }
  150.     gFontItem:        INTEGER;            { Item ID of current font in font menu        }
  151.     gFontSize:        INTEGER;            { Size of current font                        }
  152.     gNumWind:        INTEGER;            { Number of windows open                    }
  153.  
  154. PROCEDURE DoNew; FORWARD;
  155.  
  156. {
  157. #    Initialize -- Initialize toolbox managers, menus and global variables
  158. #
  159. #    Called by -- Main
  160. #
  161. #    Description -- First, the application's heap zone is expanded and a block
  162. #                of master pointers are allocated, then the toolbox managers are
  163. #                initialized.  Then the menus are called from the resource fork
  164. #                and inserted into the menu bar.  The "Font" menu is initialized
  165. #                next by looking for the defualt font in the font menu.  If the
  166. #                font is not found, then the applicationFont is used instead.
  167. #                Finally, the global variables are defined, and a new window
  168. #                is displayed.
  169. }
  170. PROCEDURE Initialize;
  171.  
  172. VAR
  173.     i:                INTEGER;        { FOR loop control variable                             }
  174.     menuID:         INTEGER;        { FOR loop control variable-used to find default font     }
  175.     dfltString:     Str255;            { The name of the default font                            }
  176.     theString:        Str255;            { Font name returned by GetItem                         }
  177.     numItems:        INTEGER;        { Number of items in "Font" menu                         }
  178.     screenArea:        Rect;            { Size of the screen                                    }
  179.     screenPort:        GrafPtr;        { Returned by GetWMgrPort                                }
  180.     
  181. BEGIN
  182. MaxApplZone;
  183. MoreMasters;                                { Get a block of master pointers    }
  184. InitGraf(@thePort);                         { Initialize QuickDraw                 }
  185. InitFonts;                                    { Initialize Font Manager             }
  186. InitCursor;                                 { Initialize Cursor (to arrow)         }
  187. InitWindows;                                { Initialize Window Manager         }
  188. InitMenus;                                    { Initialize Menu Manager             }
  189. TEInit;                                     { Initialize TextEdit                 }
  190. InitDialogs(NIL);                            { Initialize Dialog Manager         }
  191.  
  192. FlushEvents(everyEvent,0);                    { Empty the event queue in case there were events there    }
  193. GetWMgrPort(screenPort);                    { Set up the grafPort for all windows                    }
  194. SetPort(screenPort);                        { Keep the screen port on standby                         }
  195. screenArea := screenBits.bounds;            { Get the size of this Mac's screen }
  196. WITH screenArea DO
  197.     BEGIN
  198.     SetRect(gDragArea,5,                     { This sets the area in which a window can be drug around    }
  199.                      25,                    { in.  The references to right and bottom refer to the         }
  200.                      Right-5,                { record screenArea, derived from screenBits.bounds.  This     }
  201.                      Bottom-10);            { keeps a mininum amount of window on the screen.             }
  202.     SetRect(gGrowArea,65,                    { Set the area where the window can grow into--it manages    }
  203.                      65,                    { to keep the window big enough so that the close and         }
  204.                      Right-5,                { zoom boxes are still visible.                             }
  205.                      Bottom-10);            
  206.     END;
  207.  
  208. menuID := 1111;                             { The first menu's ID (the apple menu)    }
  209. FOR i := 1 to NumMenus DO                    { Put all the menus in the menu bar     }
  210.     BEGIN
  211.     gMenuItem[i] := GetMenu(menuID);            { Get the menu from the resource manager    }
  212.     menuID := menuID + 1;                    { Increment the menu ID for the next menu     }
  213.     InsertMenu(gMenuItem[i],0);                { Put the menu in the menu bar                 }
  214.     END;
  215. AddResMenu(gMenuItem[AppleID],'DRVR');        { Get DRVR resources and put them in apple menu        }
  216. gFontSMenu := GetMenu(FontSMenu);            { Get the font sub-menu                             }
  217. AddResMenu(gFontSMenu, 'FONT');                { Get FONT resources and put them in the font menu     }
  218. InsertMenu(gFontSMenu, -1);                    { Put the menu in the hierarchical menu area         }
  219.  
  220. numItems := CountMItems(gFontSMenu);        { Get the number of items in the font menu    }
  221. dfltString := 'Courier';                    { Set default string                         }
  222. FOR i := 1 TO numItems DO                    { Keep looping until the default is found     }
  223.     BEGIN
  224.     GetItem(gFontSMenu, i, theString);
  225.     IF (EqualString(theString, dfltString, false, false)) THEN
  226.         BEGIN
  227.         CheckItem(gFontSMenu, i, true);    { When the font is found, check it         }
  228.         gFontItem := i;                        { And initialize font global variables     }
  229.         gFontName := theString;
  230.         END;
  231.     END;
  232. DrawMenuBar;                                { Draw the menu bar             }
  233. gActvWindow := nil;                            { Initialize global variables     }
  234. gFontSize := 10;
  235. gFinished := false;
  236. gNumWind := 0;
  237. DoNew;                                        { Get a new window }
  238. END;
  239.  
  240. {
  241. #    DoAboutBox -- Show dialog box with program information
  242. #
  243. #    Called by -- DoAppleMenu
  244. #
  245. #    Description -- Displays a dialog box, usually containing a picture that
  246. #                gives information about the program.  The dialog terminates
  247. #                after the user clicks in the dialog.
  248. }
  249.  
  250. PROCEDURE DoAboutBox;
  251.  
  252. VAR
  253.     doneDialog:     BOOLEAN;            { True if done with dialog                                 }
  254.     doDialog:        DialogPtr;            { Pointer to "about..." dialog                             }
  255.     itemHit:        INTEGER;            { Returned by ModalDialog                                 }
  256.     thisPort:        WindowPtr;            { Graf port that is active when DoAboutBox is started    }
  257.     
  258. BEGIN
  259. doneDialog := FALSE;                        { Initialize the close dialog flag }
  260. doDialog := GetNewDialog(aboutDlog,NIL,
  261.                          POINTER(-1));        { Get the dialog box for the "about..." Window }
  262. GetPort(thisPort);
  263. SetPort(doDialog);
  264.  
  265. REPEAT
  266. ModalDialog(NIL,itemHit);                    { Find which item was hit }
  267. CASE itemHit OF
  268.     1:    doneDialog := TRUE;                    { If it was the picture, then quit }
  269.     
  270.     END; {case}
  271. UNTIL doneDialog;    
  272.  
  273. DisposDialog(doDialog);                       { Get rid of the dialog from screen and memory     }
  274. HiliteMenu(0);                                { Unhighlight the menu                             }
  275. SetPort(thisPort);                            { Reset the dialog box font back to system font }
  276. END;
  277.  
  278. {
  279. #    DoAppleMenu -- An item was selected from the Apple (DA) menu
  280. #
  281. #    Called by -- HandleMenuSelect
  282. #
  283. #    Description -- There are two cases that need to be checked.  If the itemID
  284. #                passed as a parameter is 1, then call the DoAboutBox routine.
  285. #                If the item ID is not one, then it is assumed that a Desk
  286. #                Accessory was selected, and the DA should be opened.  Note that
  287. #                item 2 should be a dashed line that is disabled; therefore
  288. #                itemID should never be 2.
  289. }
  290. PROCEDURE DoAppleMenu(itemID: INTEGER);        { itemID = menu item number }
  291.  
  292. VAR
  293.     result:         INTEGER;            { result returned by GetDeskAcc--ignored         }
  294.     dAName:         Str255;                { Name of item selected-passed to OpenDeskAcc     }
  295.  
  296. BEGIN
  297. IF itemID = 1 THEN                            { If the about... item was picked, then    }
  298.     BEGIN                                    { bring up the about box                }
  299.     DoAboutBox;
  300.     END
  301. ELSE
  302.     BEGIN                                    { Otherwise, a desk accessory was picked    }
  303.     GetItem(gMenuItem[AppleID],itemID,dAName);    { Get the name of the DA picked         }
  304.     result := OpenDeskAcc(dAName);            { And open it                                } 
  305.     HiliteMenu(0);                            { Unhilite the menu                         }
  306.     END;
  307. END; {DoAppleMenu}
  308.  
  309. {
  310. #    DoUnhilite -- Dim menu items when no list is present
  311. #
  312. #    Called by -- DoClose (only when last window is closed)
  313. #
  314. #    Description -- This routine dims menu items that can cause errors when
  315. #                selected with no list present (i.e. any menu item that calls a
  316. #                routine that expects a valid ListHandle)
  317. }
  318. PROCEDURE DoUnhilite;
  319.  
  320. BEGIN
  321. DisableItem(gMenuItem[FileID], CloseItem);    { Disable the "Close" menu item     }
  322. DisableItem(gMenuItem[FileID], AddItem);    { Disable the "Add Row" menu item    }
  323. DisableItem(gMenuItem[FileID], DeleteItem);    { Disable the "Delete Row" item        }
  324. DisableItem(gMenuItem[FileID], FindItem);    { Disable the "Find…" menu item        }
  325. END;
  326.  
  327. {
  328. #    DoHilite -- Hilite menu items when a new list is created
  329. #
  330. #    Called by -- DoNew (when no windows were present before DoNew call)
  331. #
  332. #    Description -- This routines hilites the menu items dimmed by DoUnhilite 
  333. #                when a new window is created and it is the only window (i.e.
  334. #                gNumWind = 0 before window was created)
  335. }
  336. PROCEDURE DoHilite;
  337.  
  338. BEGIN
  339. EnableItem(gMenuItem[FileID], CloseItem);    { Enable the "Close" menu item         }
  340. EnableItem(gMenuItem[FileID], AddItem);        { Enable the "Add Row" menu item    }
  341. EnableItem(gMenuItem[FileID], DeleteItem);    { Enable the "Delete Row" item        }
  342. EnableItem(gMenuItem[FileID], FindItem);    { Enable the "Find…" menu item        }
  343. END;
  344.  
  345. {
  346. #    DoNew -- Create a new window and list
  347. #
  348. #    Called by -- Initialize and DoFileMenu
  349. #
  350. #    Description -- This routine allocates space for a new document record,
  351. #                which is used in the wStorage parameter in the GetNewWindow
  352. #                call.  A new window is then created.  If one or more windows
  353. #                are present before the new window is created, the window size
  354. #                is scaled down to allow windows behind the new window to be 
  355. #                visible.  After scaling, the window is assigned a name in the
  356. #                format : Untitled—<window number>, and then the window is
  357. #                finally drawn.
  358. #
  359. #    LIST MANAGER SPECIFICS:
  360. #    ---- ------- ---------
  361. #    Setting selFlags -- Before the list is created, each of the items in the 
  362. #                List menu is looked at to see if the user has selected that 
  363. #                item.  If the item is checked, the constant representing that
  364. #                flag is added to a flag constant that is later added to the 
  365. #                selFlags field in the ListHandle.
  366. #
  367. #    Creating the list -- After the menu items have been scanned, a new list is
  368. #                created with the List Manager routine LNew.  RView is the
  369. #                enclosing rectangle of the list, which is the same as the
  370. #                window the list is in, except for a 15 pixel margin on the 
  371. #                bottom and right sides of the window to allow space for the
  372. #                scroll bars.  DataBounds is the number of cells the list is
  373. #                initialized with.  
  374. #
  375. #    Drawing the list -- Once the window and list have been drawn, the List
  376. #                manager routine LDoDraw is called, which draws the list when
  377. #                necessary.
  378. }
  379. PROCEDURE DoNew;
  380.  
  381. VAR
  382.     fNum:            INTEGER;            { The font number used for the list }    
  383.     theMark:        Char;                { Returned by GetItemMark            }
  384.     flags:            SignedByte;            { Flags for the ListHandle            }
  385.     numStr:         Str255;                { String version of window number    }
  386.     windTitle:        Str255;                { Title of the new window            }
  387.     rView:            Rect;                { Size of the initial list            }
  388.     dataBounds:        Rect;                { Number of cells in initial list    }
  389.     cSize:            Point;                { Size of the cells in the list        }
  390.     windRec:        Ptr;                { Pointer to window storage            }
  391.     
  392. BEGIN
  393. windRec := NewPtr(sizeof(DocumentRecord));    { Allocate space for a document record }
  394. gActvWindow := GetNewWindow(theWind, windRec, WindowPtr(-1));    { Get a new window }
  395. gActvWindow^.portRect.top := gActvWindow^.portRect.top + (gNumWind * 20);        { Shrink the top left part of the window     }
  396. gActvWindow^.portRect.left := gActvWindow^.portRect.left + (gNumWind * 20);    { so other windows are partially visible    }
  397. IF gNumWind = 0 THEN                        { If this is the only window, then hilite     }
  398.     DoHilite;                                { the necessary menu items                    }
  399. gNumWind := gNumWind + 1;                    { Increment the number of windows             }
  400. NumToString(LONGINT(gNumWind), numStr);
  401. SetWTitle(gActvWindow, CONCAT('Untitled—',numStr));    { Create the window title                 }
  402. gWindInfo := DocumentPeek(gActvWindow);        { Get the document record pointer                 }
  403. GetFNum(gFontName, fNum);                    { Get the font number of the current font        }
  404. gActvWindow^.txFont := fNum;                    { Set the current font                             }
  405. gActvWindow^.txSize := gFontSize;            { And set the global var                        }
  406. SetPort(gActvWindow);                        { Set the port to our new window                }
  407. rView := gActvWindow^.portRect;                { Create the rView rectangle for the LNew call    }
  408. rView.right := rView.right - 15;            { Remember to allow enough space for the        }
  409. rView.bottom := rView.bottom - 15;            {   list's scroll bars                            }
  410. ShowWindow(gActvWindow);                        { Make the window visible                         }
  411. SetRect(dataBounds, 0, 0, 10, 1);            { Set the number of default cells                 }
  412. SetPt(cSize, 100, 16);                        { Set the default cell size                     }
  413.  
  414. flags := 0;                                    { Initialize the flags }
  415. GetItemMark(gMenuItem[ListID], 1, theMark);
  416. IF (theMark = CHR(checkMark)) THEN            { Is the lOnlyOne flag set?        }
  417.     flags := flags + lOnlyOne;                { If so, add it to the flags    }
  418. GetItemMark(gMenuItem[ListID], 2, theMark);    
  419. IF (theMark = CHR(checkMark)) THEN            { Is the lExtendDrag flag set?    }
  420.     flags := flags + lExtendDrag;            { If so, add it to the flags     }
  421. GetItemMark(gMenuItem[ListID], 3, theMark);
  422. IF (theMark = CHR(checkMark)) THEN            { Is the lNoDisjoint flag set?    }
  423.     flags := flags + lNoDisjoint;            { If so, add it to the flags    }
  424. GetItemMark(gMenuItem[ListID], 4, theMark);
  425. IF (theMark = CHR(checkMark)) THEN            { Is the lNoExtend flag set?    }
  426.     flags := flags + lNoExtend;                { If so, add it to the flags    }
  427. GetItemMark(gMenuItem[ListID], 5, theMark);
  428. IF (theMark = CHR(checkMark)) THEN            { Is the lNoRect flag set?         }
  429.     flags := flags + lNoRect;                { If so, add it to the flags    }
  430. GetItemMark(gMenuItem[ListID], 6, theMark);
  431. IF (theMark = CHR(checkMark)) THEN            { Is the lUseSense flag set?     }
  432.     flags := flags + lUseSense;                { If so, add it to the flags     }
  433. GetItemMark(gMenuItem[ListID], 7, theMark);
  434. IF (theMark = CHR(checkMark)) THEN            { Is the lNoNilHilite flag set?    }
  435.     flags := flags + lNoNilHilite;            { If so, add it to the flags     }
  436. gWindInfo^.docList := LNew(rView, dataBounds, cSize, 0,
  437.                           gActvWindow, true, false, true, true); { Create the new list     }
  438. gWindInfo^.docList^^.selFlags := flags;        { Set the selFlags field                    }
  439. LDoDraw(true, gWindInfo^.docList);            { Allow drawing                                }
  440. END;
  441.  
  442. {
  443. #    DoClose -- Handle closing a window and disposing of its contents
  444. #
  445. #    Called by -- DoFileMenu and HandleMouseDown
  446. #
  447. #    Description -- This routine is called when "Close" is selected from the
  448. #                "File" menu, or the mouse is clicked in the go away region of 
  449. #                the window.  Any contents of the window should be handled
  450. #                (i.e. disposed) before the window is closed.  Before doing any
  451. #                closing, the windowKind field should be checked.  If it is
  452. #                negative, the window to be closed belongs to a desk accessory.
  453. #                When this happens, call CloseDeskAcc and pass the value in 
  454. #                windowKind.  If a window is closed, and no other windows belong
  455. #                to the application, the gNumWind variable is set to zero, and
  456. #                the DoUnhilite procedure is called to dim the necessary menu
  457. #                items.
  458. #
  459. #    LIST MANAGER SPECIFICS
  460. #    ---- ------- ---------
  461. #    Disposing of the list -- Before closing the window, you need to call
  462. #                LDispose to get rid of the list.
  463. }
  464. PROCEDURE DoClose;
  465.  
  466. VAR
  467.     testWindow:     WindowPtr;            { The active window }
  468.     whatWindow:     DocumentPeek;        { The active window's window record }
  469.     
  470. BEGIN
  471. testWindow := FrontWindow;                    { Get the active window }
  472. whatWindow := DocumentPeek(testWindow);     { Coerce the window record }
  473. IF whatWindow^.docWindow.windowKind > 0 THEN          { If it is an application window then }
  474.     BEGIN    
  475.     LDispose(whatWindow^.docList);            { Get rid of the list }
  476.     CloseWindow(FrontWindow);                  { Get rid of the window }
  477.     gActvWindow := FrontWindow;                { Set the active window to the frontmost window }
  478.     IF gActvWindow = NIL THEN                { If there are no windows then }
  479.         BEGIN
  480.         gNumWind := 0;                        { Reset the window counter }
  481.         DoUnhilite;                            { Dim the necessary menus }
  482.         END
  483.     ELSE                                    { Otherwise, there are windows open }
  484.         BEGIN
  485.         SelectWindow(gActvWindow);            { Select the active window }
  486.         gWindInfo := DocumentPeek(gActvWindow);    { Get the new Document Record }
  487.         END 
  488.     END
  489. ELSE                                        { If the window being closed was a DA window }
  490.     CloseDeskAcc(whatWindow^.docWindow.windowKind);   { Then close the desk accessory }
  491. HiliteMenu(0);
  492. END;
  493.  
  494. {
  495. #    DoQuit -- Stop the application
  496. #
  497. #    Called by -- DoFileMenu
  498. #
  499. #    Description -- The main function of this routine is to set the "finished"
  500. #                flag so the main event loop will stop looping.  I also close
  501. #                all the windows and it's associated items.  I know that I don't
  502. #                have to do this because the system will get rid of all the
  503. #                application's memory when it quits, but hey, I like to clean
  504. #                up afterwards.
  505. #
  506. #    LIST MANAGER SPECIFICS
  507. #    ---- ------- ---------
  508. #    Disposing of the list -- BEFORE closing the window, you must get rid of the
  509. #                list by calling LDispose.
  510. }
  511. PROCEDURE DoQuit;
  512.  
  513. VAR
  514.     errNo:                INTEGER;        { Returned by FSClose                }
  515.     testWindow:         WindowPtr;        { Frontmost window                    }
  516.     windPeek:            DocumentPeek;    { Document record of top window        }
  517.     stopLoop:            Boolean;        { Flag to stop window-closing loop    }
  518.     
  519. BEGIN
  520. stopLoop := false;                            { Initialize loop flag }
  521. testWindow := FrontWindow;                    { Get the top window }
  522. windPeek := DocumentPeek(testWindow);        { Get the top window's doc record }
  523. IF testWindow <> NIL THEN                    { If there is a window active }
  524.     BEGIN
  525.     REPEAT
  526.         LDispose(windPeek^.docList);        { Get rid of the list }
  527.         CloseWindow(testWindow);            { Get rid of the window }
  528.         testWindow := FrontWindow;            { Get the top window, if any }
  529.         IF testWindow <> nil THEN            { If there is a window }
  530.             windPeek := DocumentPeek(testWindow)    { Get it's doc record }
  531.         ELSE                                { If there are no more windows }
  532.             stopLoop := true;                { Stop looping }
  533.     UNTIL stopLoop;
  534.     END;
  535. gFinished := TRUE;                            { If there were no open windows, just stop the application }
  536. END;
  537.  
  538. {
  539. #    DoFind -- A List Manager specific routine that searches for data in a cell
  540. #
  541. #    Called By -- DoFileMenu
  542. #
  543. #    Description -- This routine puts up a dialog box that the user will type
  544. #                the text to be found into.  After the user clicks the OK button,
  545. #                the List Manager routine LSearch to find the cell (if any) that
  546. #                data is located in.  Note that theCell must be initialized to
  547. #                (0,0) because the LSearch routine starts searching at theCell.
  548. }
  549. PROCEDURE DoFind;
  550.  
  551. VAR
  552.     theDialog:            DialogPtr;        { Pointer to the dialog box             }
  553.     itemHit:            INTEGER;        { Item the mouse is clicked in             }
  554.     doneDialog:         Boolean;        { Modal Dialog loop flag                 }
  555.     theType:            INTEGER;        { Returned by GetDItem--not used         }
  556.     dataLen:            INTEGER;        { Length of the find text                 }
  557.     theCell:            Cell;            { The cell the text is found in            }
  558.     item:                Handle;            { A handle to the dialog editText item    }
  559.     box:                Rect;            { Returned by GetDItem--not used        }
  560.     theString:            Str255;            { The find text                            }
  561.     dStorage:            DialogRecord;    { The dialog record                        }
  562.  
  563. BEGIN
  564. theDialog := GetNewDialog(findDlog, @dStorage, DialogPtr(-1));    { Get the dialog }
  565. doneDialog := false;                        { Initialize the repeat loop flag }
  566. REPEAT
  567.     ModalDialog(nil, itemHit);                { Get the item the mouse was clicked in }
  568.     CASE (itemHit) OF
  569.         1:    BEGIN                            { The OK button }
  570.             doneDialog := true;                { Stop looping }
  571.             GetDItem(theDialog, 2, theType, item, box);    { Get the editText item }
  572.             GetIText(item, theString);        { Get the find text }
  573.             dataLen := Length(theString);    { Get the length of the text }
  574.             SetPt(theCell, 0, 0);            { Initialize the cell    }
  575.             IF (LSearch(POINTER(ORD(@theString)+1), dataLen, nil, theCell, gWindInfo^.docList)) THEN
  576.                 LSetSelect(true, theCell, gWindInfo^.docList)
  577.             ELSE
  578.                 SysBeep(20);                { Beep if nothing was found }
  579.             END;
  580.     END; {Case}
  581. UNTIL doneDialog;
  582. CloseDialog(theDialog);                        { Get rid of the dialog }
  583. END;
  584.  
  585. {
  586. #    DoFileMenu -- Respond to the user selecting an item in the File Menu
  587. #
  588. #    Called By -- HandleMenuSelect
  589. #
  590. #    Description -- This routine is simply a case statement that calls a routine
  591. #                that handles the selected item
  592. #
  593. #    LIST MANAGER SPECIFICS
  594. #    ---- ------- ---------
  595. #    Adding a row -- When the itemID "AddItem" is passed in the item ID parameter,
  596. #                the case statement simply calls LAddRow to add another row.  The
  597. #                rowNum parameter is passed 32767 to ensure that the row is added
  598. #                to the end of the list.
  599. #    Deleting a row -- When the item ID "DeleteItem" is passed in the item ID
  600. #                parameter, the case statement finds which cell (if any) is
  601. #                selected, then calls LDelRow to delete the row the selected
  602. #                cell is in.
  603. }
  604. PROCEDURE DoFileMenu(itemID: INTEGER);
  605.  
  606. VAR
  607.     newRow:         INTEGER;            { Number of the new row }
  608.     theCell:        Cell;                { Generic cell variable    }
  609.     
  610. BEGIN
  611. CASE itemID OF
  612.     NewItem:    DoNew;                        { Get a new list }
  613.     
  614.     CloseItem:    DoClose;                    { Close a list }
  615.     
  616.     AddItem: newRow := LAddRow(1, 32767, gWindInfo^.docList);    { Add a new row }
  617.     
  618.     DeleteItem:                                { Get rid of a new row }
  619.         BEGIN
  620.         SetPt(theCell, 0, 0);                { Initialize theCell to the first cell in the list }
  621.         IF (LGetSelect(true, theCell, gWindInfo^.docList)) THEN    { If any cell is selected, then }
  622.             LDelRow(1, theCell.v, gWindInfo^.docList);            { Delete the row the first selected cell is in }
  623.         END;
  624.     
  625.     FindItem:    DoFind;                        { Find data in a cell }
  626.  
  627.     Quit:        DoQuit;                        { Quit the program }
  628.  
  629.     END;
  630. HiliteMenu(0);
  631. END; {DoFileMenu}
  632.  
  633. {
  634. #    DoEditMenu -- Respond to the user selecting an item from the Edit menu
  635. #
  636. #    Called By -- HandleMenuSelect
  637. #
  638. #    Description -- The routine is a case statement that handles all the items
  639. #                in the edit menu.
  640. #
  641. #    LIST MANAGER SPECIFICS
  642. #    ---- ------- ---------
  643. #    Cutting a cell -- Cut was implemented by using LGetCell to get the data, 
  644. #                then calling LClrCell to clear the cell.
  645. #    Copying a cell -- Same as cutting, but LClrCell wasn't called.
  646. #    Pasting a cell -- Paste was implemented by using LSetCell.
  647. #    Clearing a cell -- Clear was implemented by using LClrCell.
  648. #    About LGetSelect -- Before LGetSelect is called, the theCell parameter is
  649. #                initialized to the first cell in the list (0,0).  The reason
  650. #                for this is that LGetSelect finds the first cell that is
  651. #                hilighted that is *equal to or greater than* the theCell
  652. #                parameter.  Therefore, if you want to find the first cell in
  653. #                the entire list, set the theCell parameter to (0,0).
  654. }
  655. PROCEDURE DoEditMenu(itemID: INTEGER);
  656.  
  657. VAR
  658.     theCell:        Cell;                { Generic cell variable }
  659.     dataLen:        INTEGER;            { Length of data in our private scrap }
  660.  
  661. BEGIN
  662. IF (gActvWindow <> NIL) THEN                    { If a window exists, then        }
  663.     CASE itemID OF                            { Find which item was selected    }
  664.         UndoItem: ;                            { There is no undo                }
  665.         
  666.         CutItem:                            { Cut data from a cell }
  667.             BEGIN
  668.             SetPt(theCell, 0, 0);            { Initialize theCell to the first item }
  669.             IF (LGetSelect(true, theCell, gWindInfo^.docList)) THEN    { Find the cell that is selected }
  670.                 BEGIN
  671.                 gClipLen := 256;                                        { Initialize the scrap length }
  672.                 LGetCell(POINTER(ORD(@gClipboard)+1), gClipLen, theCell, gWindInfo^.docList);    { Get the data }
  673.                 LClrCell(theCell, gWindInfo^.docList);                { Clear the data    }
  674.                 LDraw(theCell, gWindInfo^.docList);                    { Redraw the cell    }
  675.                 END
  676.             ELSE
  677.                 SysBeep(20);
  678.             END;
  679.         
  680.         CopyItem:                            { Copy data from a cell    }
  681.             BEGIN
  682.             SetPt(theCell, 0, 0);
  683.             IF (LGetSelect(true, theCell, gWindInfo^.docList)) THEN
  684.                 BEGIN
  685.                 gClipLen := 256;                                            
  686.                 LGetCell(POINTER(ORD(@gClipboard)+1), gClipLen, theCell, gWindInfo^.docList);
  687.                 LDraw(theCell, gWindInfo^.docList);
  688.                 END
  689.             ELSE
  690.                 SysBeep(20);
  691.             END;
  692.         
  693.         ClearItem:                            { Clear data from the cell }
  694.             BEGIN
  695.             SetPt(theCell, 0, 0);
  696.             IF (LGetSelect(true, theCell, gWindInfo^.docList)) THEN
  697.                 BEGIN
  698.                 gClipLen := 256;
  699.                 LClrCell(theCell, gWindInfo^.docList);
  700.                 LDraw(theCell, gWindInfo^.docList);
  701.                 END
  702.             ELSE
  703.                 SysBeep(20);
  704.             END;
  705.  
  706.         PasteItem:                        { Paste data into a cell    }
  707.             BEGIN
  708.             SetPt(theCell, 0, 0);
  709.             IF (LGetSelect(true, theCell, gWindInfo^.docList)) THEN
  710.                 BEGIN
  711.                 LSetCell(POINTER(ORD(@gClipboard)+1), gClipLen, theCell, gWindInfo^.docList);
  712.                 LDraw(theCell, gWindInfo^.docList);
  713.                 END
  714.             ELSE
  715.                 SysBeep(20);
  716.             END;
  717.     END;
  718. HiliteMenu(0);
  719. END;
  720.  
  721. {
  722. #    DoListMenu -- Respond to the user selecting an item from the List menu
  723. #
  724. #    Called By -- HandleMenuSelect
  725. #
  726. #    Description -- This routine simply checks or unchecks the menu item passed
  727. #                as a parameter.  When a new list is created, these menu items
  728. #                are polled to see which flags should be set.
  729. }
  730. PROCEDURE DoListMenu(itemID: INTEGER);
  731.  
  732. VAR
  733.     theMark:            CHAR;            { Mark returned by GetItemMark }
  734.  
  735. BEGIN
  736. GetItemMark(gMenuItem[ListID], itemID, theMark);        { Get the mark of the item        }
  737. IF theMark = CHR(checkMark) THEN                    { If it is a check mark then    }
  738.     CheckItem(gMenuItem[ListID], itemID, false)        { Uncheck the item                }
  739. ELSE
  740.     CheckItem(gMenuItem[ListID], itemID, true);        { Otherwise check it }
  741. HiliteMenu(0);
  742. END;
  743.  
  744. {
  745. #    DoFontMenu -- Respond to a user selecting an item in the Font sub menu
  746. #
  747. #    Called By -- HandleMenuSelect
  748. #
  749. #    Description -- When an item is selected, the "old" Font name is unchecked,
  750. #                the new font is set, and it checked in the menu.  Then the
  751. #                font name and item global variables are set.
  752. }
  753. PROCEDURE DoFontMenu(itemID: INTEGER);
  754.  
  755. VAR
  756.     theString:        Str255;                { Name of the font selected }
  757.  
  758. BEGIN
  759. CheckItem(gFontSMenu, gFontItem, false);    { Uncheck the current font name        }
  760. GetItem(gFontSMenu, itemID, theString);        { Get the name of the selected font }
  761. CheckItem(gFontSMenu, itemID, true);        { Check the item                    }
  762. gFontName := theString;                        { Set the global font var            }
  763. gFontItem := itemID;                        { Set the global item id            }
  764. HiliteMenu(0);
  765. END;
  766.  
  767. {
  768. #    DoCellMenu -- Respond to the user selecting an item in the Cell menu
  769. #
  770. #    Called by -- HandleMenuSelect
  771. #
  772. #    Description -- This routine sends the itemID to a case statement where it
  773. #                is handled.  If the itemID is "IndentItem", then the vertical
  774. #                and horizontal coordinates are retrieved from the dialog box
  775. #                used to ask the user for the coordinates, then sets the indent
  776. #                field of the ListHandle to those coordinates.  If itemID is
  777. #                CSizeItem, then the coordinates of the cell size are retrieved
  778. #                from the dialog box, then set by using LCellSize.  If itemID is
  779. #                FSizeItem, then the font size entered in the dialog box is
  780. #                set in the txSize field of the grafPort.  This change does not
  781. #                take affect until a new list is created.
  782. }
  783. PROCEDURE DoCellMenu(itemID: INTEGER);
  784.  
  785. VAR
  786.     theString:            Str255;            { String returned by GetIndString/GetIText    }
  787.     dStorage:            DialogRecord;    { Storage for the dialog record                }
  788.     theDialog:            DialogPtr;        { Pointer to the dialog                        }
  789.     theType:            INTEGER;        { Not used--passed to GetDItem                }
  790.     item:                Handle;            { Handle to item, returned by GetDItem        }
  791.     box:                Rect;            { Not used--passed to GetDItem                }
  792.     origIndent:         Point;            { Data indent before changing                }
  793.     vertIndent:         LONGINT;        { Vertical indentation set by user            }
  794.     horizIndent:        LONGINT;        { Horizontal indentation set by user        }
  795.     itemHit:            INTEGER;        { Item number returned by ModalDialog        }
  796.     doneDialog:         Boolean;        { Flag is set when user is done with dialog    }
  797.     vertItem:            Handle;            { Handle to vertical indent. text box        }
  798.     horizItem:            Handle;            { Handle to horizontal indent. text box        }
  799.  
  800. BEGIN
  801. doneDialog := false;                        { Initialize the doneDialog flag    }
  802. CASE itemID OF
  803.     IndentItem:                                { Set the indent field of the List Handle }
  804.         BEGIN
  805.         IF (gActvWindow <> NIL) THEN            { If a window is present            }
  806.             BEGIN
  807.             { Draw a dialog and set the editText items }
  808.             theDialog := GetNewDialog(cellDlog, @dStorage, DialogPtr(-1));
  809.             GetDItem(theDialog, 4, theType, item, box);
  810.             GetIndString(theString, theStrList, vertInd);
  811.             SetIText(item, theString);
  812.             GetDItem(theDialog, 5, theType, item, box);
  813.             GetIndString(theString, theStrList, horizInd);
  814.             SetIText(item, theString);
  815.             GetDItem(theDialog, 6, theType, item, box);
  816.             GetIndString(theString, theStrList, indentInd);
  817.             SetIText(item, theString);
  818.             origIndent := gWindInfo^.docList^^.indent;
  819.             vertIndent := origIndent.v;
  820.             horizIndent := origIndent.h;
  821.             GetDItem(theDialog, 2, theType, vertItem, box);
  822.             NumToString(vertIndent, theString);
  823.             SetIText(vertItem, theString);
  824.             GetDItem(theDialog, 3, theType, horizItem, box);
  825.             NumToString(horizIndent, theString);
  826.             SetIText(horizItem, theString);
  827.             ShowWindow(theDialog);
  828.             REPEAT
  829.                 { When an event occurs, check if the OK button was hit }
  830.                 ModalDialog(nil, itemHit);
  831.                 CASE itemHit OF
  832.                     1:    BEGIN
  833.                         { If the OK button was hit, get the editText items, and set the indent field }
  834.                         doneDialog := true;
  835.                         GetIText(vertItem, theString);
  836.                         StringToNum(theString, vertIndent);
  837.                         GetIText(horizItem, theString);
  838.                         StringToNum(theString, horizIndent);
  839.                         SetPt(origIndent, LoWord(horizIndent), LoWord(vertIndent));
  840.                         gWindInfo^.docList^^.indent := origIndent;
  841.                         LUpdate(gActvWindow^.visRgn, gWindInfo^.docList);
  842.                         END;
  843.                 END;
  844.             UNTIL doneDialog;
  845.             CloseDialog(theDialog);
  846.             END
  847.         ELSE
  848.             SysBeep(20);
  849.         END;
  850.     
  851.     CSizeItem:                                { Set the cell size }
  852.         BEGIN
  853.         IF (gActvWindow <> NIL) THEN            { IF there is a window }
  854.             BEGIN
  855.             { Draw the dialog and set editText items }
  856.             theDialog := GetNewDialog(cellDlog, @dStorage, DialogPtr(-1));
  857.             GetDItem(theDialog, 4, theType, item, box);
  858.             GetIndString(theString, theStrList, vertInd);
  859.             SetIText(item, theString);
  860.             GetDItem(theDialog, 5, theType, item, box);
  861.             GetindSTring(theString, theStrList, horizInd);
  862.             SetIText(item, theString);
  863.             GetDItem(theDialog, 6, theType, item, box);
  864.             GetIndString(theString, theStrList, cSizeInd);
  865.             SetIText(item, theString);
  866.             origIndent := gWindInfo^.docList^^.cellSize;
  867.             vertIndent := origIndent.v;
  868.             horizIndent := origIndent.h;
  869.             GetDItem(theDialog, 2, theType, vertItem, box);
  870.             NumToString(vertIndent, theString);
  871.             SetIText(vertItem, theString);
  872.             GetDItem(theDialog, 3, theType, horizItem, box);
  873.             NumToString(horizIndent, theString);
  874.             SetIText(horizItem, theString);
  875.             ShowWindow(theDialog);
  876.             REPEAT
  877.                 ModalDialog(nil, itemHit);
  878.                 CASE itemHit OF
  879.                     1:    BEGIN
  880.                         { IF the OK button was hit, get the editText items the user set,
  881.                           and call LCellSize to change the cell size }
  882.                         doneDialog := true;
  883.                         GetIText(vertItem, theString);
  884.                         StringToNum(theString, vertIndent);
  885.                         GetIText(horizItem, theString);
  886.                         StringToNum(theString, horizIndent);
  887.                         SetPt(origIndent, LoWord(horizIndent), LoWord(vertIndent));
  888.                         LCellSize(origIndent, gWindInfo^.docList);
  889.                         END;
  890.                 END;
  891.             UNTIL doneDialog;
  892.             CloseDialog(theDialog);
  893.             END
  894.         ELSE
  895.             SysBeep(20);
  896.         END;
  897.     
  898.     FSizeItem:                            { Set the font size    }
  899.         BEGIN
  900.         theDialog := GetNewDialog(fontSDlog, @dStorage, DialogPtr(-1));
  901.         GetDItem(theDialog, 2, theType, item, box);
  902.         SetIText(item, '10');
  903.         ShowWindow(theDialog);
  904.         REPEAT
  905.             ModalDialog(nil, itemHit);
  906.             CASE itemHit OF
  907.                 1:    BEGIN
  908.                     { IF the OK button was hit, get the editText item and set the font size }
  909.                     doneDialog := true;
  910.                     GetIText(item, theString);
  911.                     StringToNum(theString, horizIndent);
  912.                     gFontSize := LoWord(horizIndent);
  913.                     END;
  914.             END; {case}
  915.         UNTIL doneDialog;
  916.         CloseDialog(theDialog);
  917.         END;
  918.     END;
  919. HiliteMenu(0);
  920. END;
  921.  
  922. {
  923. #    HandleMenuSelect -- Determines which menu was selected from
  924. #
  925. #    Called By -- HandleMouseDown/HandleKeyDown
  926. #
  927. #    Description -- This routines finds the menuID and itemID of the item
  928. #                selected by the user.  The menu information is passed in the
  929. #                parameter menuInfo.  The high-order word contains the menuID
  930. #                and the low-order word holds the itemID.  These items are
  931. #                extracted by using HiWord and LoWord, respectively.  The menuID
  932. #                is sent to a case statement which calls a routine that handles
  933. #                that menu.
  934. }
  935. PROCEDURE HandleMenuSelect(menuInfo: LONGINT);
  936.  
  937. VAR
  938.     menuID:         INTEGER;            { Menu's resource ID }
  939.     itemID:         INTEGER;            { Item's position in menu }
  940.     
  941. BEGIN
  942. menuID := HiWord(menuInfo);                 { Get the selected menu ID }
  943. itemID := LoWord(menuInfo);                 { Get the item ID }
  944.  
  945. IF menuID <> 0 THEN                         { if MenuSelect returns a 0 in the high order integer,
  946.                                               it means that the mouse was released outside the menu }
  947.     CASE menuID OF
  948.     
  949.         AppleMenu:    DoAppleMenu(itemID);
  950.         
  951.         FileMenu:    DoFileMenu(itemID);
  952.         
  953.         EditMenu:    DoEditMenu(itemID);
  954.         
  955.         ListMenu:    DoListMenu(itemID);
  956.         
  957.         CellMenu:    DoCellMenu(itemID);
  958.         
  959.         FontSMenu:    DoFontMenu(itemID);
  960.     END;
  961. END;
  962.  
  963. {
  964. #    HandleContent -- Respond to a mouse event inside a window
  965. #
  966. #    Called By -- HandleMouseDown
  967. #    
  968. #    Description -- When a mouse-down event occurs in the content portion of a
  969. #                window (when FindWindow returns inContent), the application must
  970. #                take appropriate action:
  971. #                    1. Make The Window Active - If the window passed in the
  972. #                        whichWindow parameter is not the active window, then
  973. #                        it must be made active by calling SelectWindow.  
  974. #                        The global window pointer and document record must
  975. #                        reflect the changes.
  976. #                    2. If the window is already active, you need to find if
  977. #                        the click occurred inside an object in the window
  978. #                        such as a TextEdit box, control, or indicator.  Note
  979. #                        that the TextEdit, Control and List Managers
  980. #                        (among others) require the mouse down loaction in
  981. #                        local coordinates, so the routine GlobalToLocal
  982. #                        is called to get the local point coordinates.
  983. #
  984. #    LIST MANAGER SPECIFICS
  985. #    ---- ------- ---------
  986. #    Click occurs in the list -- A check must be made to see if the mouse down
  987. #                occurred in the list.  You do this by getting the list rect
  988. #                from the rView field in the ListHandle and calling PtInRect.
  989. #                Remember that rView returns the rect coordinates in the LOCAL
  990. #                coordinate system, so the point returned by GlobalToLocal
  991. #                should be used.
  992. #                    If the mouse down was in the list, then call the List 
  993. #                Manager routine LClick.  This routine will handle any actions
  994. #                needed in the list (selecting, moving scroll bars) according
  995. #                to the list definition procedure (LDEF).
  996. #                    IMPORTANT NOTE:  The rView rectangle DOES NOT include the
  997. #                scroll bars.  Before using PtInRect to find if the click 
  998. #                occurred in the list, add 15 to the bottom and right fields of
  999. #                the rect to include the scroll bars.  If you don't do this, the
  1000. #                list will not scroll.
  1001. }
  1002. PROCEDURE HandleContent(whichWindow :WindowPtr);
  1003.  
  1004. VAR
  1005.     thePoint:        Point;                { events.where in local coordinates    }
  1006.     theRect:        Rect;                { The area the list occupies        }
  1007.     yorn:            BOOLEAN;            { Junk result from LClick            }
  1008.     
  1009. BEGIN
  1010. gActvWindow := FrontWindow;                    { Get the frontmost window                            }
  1011. thePoint := gEvents.where;                    { Set up a point to be converted to local coords    }
  1012. GlobalToLocal(thePoint);                    { Convert to local coordinates                        }
  1013. IF whichWindow <> gActvWindow THEN            { If the window selected in not the front window    }
  1014.     BEGIN
  1015.     SelectWindow(whichWindow);                { Select it                        }
  1016.     gActvWindow := whichWindow;                { Set the new active window        }
  1017.     gWindInfo := DocumentPeek(gActvWindow);    { Set the window information    }
  1018.     END;
  1019. IF gWindInfo^.docList^ <> nil THEN            { If the list is active }
  1020.     BEGIN
  1021.     theRect := gWindInfo^.docList^^.rView;    { Get the rectangle of the list                            }
  1022.     theRect.right := theRect.right + 15;    { Account for the vertical and                            }
  1023.     theRect.bottom := theRect.bottom + 15;    { horizontal scroll bars                                }
  1024.     IF PtInRect(thePoint, theRect) THEN        { If the mouse click was in the list rectangle, then    }
  1025.         yorn := LClick(thePoint, gEvents.modifiers, gWindInfo^.docList)    { Tell the list it was clicked in }
  1026.     END;
  1027. END;
  1028.  
  1029. {
  1030. #    HandleGrow -- Respond to a mouse down event in the grow icon
  1031. #
  1032. #    Called by -- HandleMouseDown
  1033. #
  1034. #    Description -- When a mouse down event occurs in the grow icon (when
  1035. #                FindWindow returns inGrow) the application must grow the
  1036. #                window, following the movements of the mouse.  This i
  1037. #                this is accomplished by calling GrowWindow, which returns the
  1038. #                new width and height of the window (or 0 if no change was
  1039. #                made).  The width is in the low-order word of the long result
  1040. #                and the height is in the high-order word.  These values are
  1041. #                extracted using LoWord and HiWord.  After getting this result,
  1042. #                SizeWindow is called, passing the height and width returned by
  1043. #                GrowWindow.  After this is done, the window is marked as "dirty"
  1044. #                and updated by calling InvalRect.
  1045. #
  1046. #    LIST MANAGER SPECIFICS
  1047. #    ---- ------- ---------
  1048. #    Growing the list -- You can use the height and width values returned by
  1049. #                GrowWindow in the ListManager routine LSize, remembering to
  1050. #                subtract room for the scroll bars, if any.
  1051. }
  1052. PROCEDURE HandleGrow(whichWindow: WindowPtr);
  1053.  
  1054. VAR
  1055.     windSize:        LONGINT;            { Gets the new size of a window when it is grown }
  1056.     vert:            INTEGER;            { The vertical component of the growth in windSize }
  1057.     horiz:            INTEGER;            { The horizontal component of the growth in windSize }
  1058.     
  1059. BEGIN
  1060. windSize := GrowWindow(whichWindow,gEvents.where,gGrowArea);    { Grow the window        }
  1061. vert := HiWord(windSize);                    { Get the vertical displacement            }
  1062. horiz := LoWord(windSize);                    { Get the horizontal displacement        }
  1063. SizeWindow(whichWindow,horiz,vert,TRUE);    { Make the changes to the window        }
  1064. LSize(horiz-15, vert-15, gWindInfo^.docList);{ Make the changes to the list            }
  1065. InvalRect(whichWindow^.portRect);            { Get the new window ready for updating    }
  1066. END;
  1067.  
  1068. {
  1069. #    HandleMouseDown -- Take care of a mouse-down event.
  1070. #
  1071. #    Called by -- Main program
  1072. #
  1073. #    Description --  The major block of the program is a case statement that
  1074. #                branches to the constant returned by the window manager 
  1075. #                function FindWindow.  Most of the events are handled inside the
  1076. #                case statement; a few have external routines.  The routines
  1077. #                that are called from this routine are:
  1078. #                    HandleMenuSelect    (inMenuBar)
  1079. #                    HandleContent        (inContent)
  1080. #                    HandleGrow            (inGrow)
  1081. }
  1082. PROCEDURE HandleMouseDown;
  1083.  
  1084. VAR
  1085.     whichWindow:    WindowPtr;            { Pointer to window returned by FindWindow    }
  1086.     windPeek:        DocumentPeek;        { Information about window                    }
  1087.     menuInfo:        LONGINT;            { Menu information returned by MenuSelect    }
  1088.     dPartCode:        INTEGER;            { Part code returned by FindWindow            }
  1089.     width:            INTEGER;            { Vertical change made by zooming            }
  1090.     height:            INTEGER;            { Horizontal change made by zooming            }
  1091.     zoomRect:        Rect;                { Window rect after zooming                    }
  1092.     
  1093. BEGIN
  1094. dPartCode := FindWindow(gEvents.where, whichWindow);    { Find which part of the window the click occurred in }
  1095.  
  1096. CASE dPartCode OF
  1097.     inMenuBar:                                { Click in the menu bar        }
  1098.         BEGIN
  1099.         menuInfo := MenuSelect(gEvents.where);    { Get menuID, itemID    }
  1100.         HandleMenuSelect(menuInfo);
  1101.         END;
  1102.  
  1103.     inContent:    HandleContent(whichWindow);    { Click in the content region }
  1104.  
  1105.     inDrag:                                         { Click in the drag region }
  1106.         BEGIN
  1107.         DragWindow(whichWindow,gEvents.where,gDragArea);
  1108.         END;
  1109.  
  1110.     inGrow:     HandleGrow(whichWindow);            { Click in grow (lower left corner) box }
  1111.     
  1112.     inGoAway:                                        { Click in the go away box }
  1113.         BEGIN
  1114.         IF TrackGoAway(whichWindow,gEvents.where) THEN
  1115.             DoClose;
  1116.         END;
  1117.     
  1118.     inSysWindow:   SystemClick(gEvents,whichWindow); { Click in desktop or DA }
  1119.     
  1120.     inZoomIn, inZoomOut:                            { Click in zoom box }
  1121.         BEGIN
  1122.         IF TrackBox(whichWindow,gEvents.where,dPartCode) THEN { If the mouse was pressed and released in the zoom box }
  1123.             BEGIN
  1124.             windPeek := DocumentPeek(whichWindow);
  1125.             ZoomWindow(whichWindow,dPartCode,TRUE);      { Zoom the window in, or out, depending upon value of inout }
  1126.             zoomRect := whichWindow^.portRect;
  1127.             width := zoomRect.right - zoomRect.left;
  1128.             height := zoomRect.bottom - zoomRect.top;
  1129.             LSize(width-15, height-15, windPeek^.docList);
  1130.             InvalRect(whichWindow^.portRect);         { Mark the window as needing an update }
  1131.             END;
  1132.         END;
  1133.     END; {case FindWindow}
  1134. END; { CASE mouseDown }
  1135.  
  1136. {
  1137. #    HandleUpdateEvt -- Do a window update
  1138. #
  1139. #    Called by -- main event loop
  1140. #
  1141. #    Description -- When GetNextEvent returns updateEvt, this routine is called
  1142. #                to redraw the contents of a window.
  1143. #
  1144. #    LIST MANAGER SPECIFICS
  1145. #    ---- ------- ---------
  1146. #    LUpdate -- The List Manager routine LUpdate is called to re-draw the window
  1147. #                that needs updating.
  1148. }
  1149. PROCEDURE HandleUpdateEvt;
  1150.  
  1151. VAR
  1152.     yorn:            INTEGER;            { FOR loop control variable        }
  1153.     theWindow:        WindowPtr;            { Window to be updated            }
  1154.     theList:        DocumentPeek;        { Information about the window    }
  1155.     savePort:        GrafPtr;            { Active port when called         }
  1156.     
  1157. BEGIN
  1158. theWindow := WindowPtr(gEvents.message); { Get the window referenced by the event by coercing it }
  1159. theList := DocumentPeek(theWindow);
  1160. GetPort(savePort);                        { save the current port                    }
  1161. SetPort(theWindow);                     { set the port to the updated window    }
  1162. BeginUpdate(theWindow);                 { Starting the update                    }
  1163.     EraseRect(theWindow^.portRect);        { Erase the window                        }
  1164.     LUpdate(theWindow^.visRgn, theList^.docList);    { Update the List            }
  1165.     DrawGrowIcon(theWindow);            { Draw the grow icon                    }
  1166. EndUpdate(theWindow);                    { Ending the update                        }
  1167. SetPort(SavePort);                        { Restoring the port                    }
  1168.  
  1169. END;
  1170.  
  1171. {
  1172. #    HandleActivateEvt -- Make a window active, deactivate another window
  1173. #
  1174. #    Called by -- main event loop
  1175. #
  1176. #    Description -- When an activate event is needed, GetNextEvent returns
  1177. #                activateEvt, plus a flag in the modifiers field.  If the flag
  1178. #                is an activate flag (bit 0 is 1 -- see Inside Mac), then
  1179. #                a grow icon (if any) is drawn, and scroll bars (if any) are
  1180. #                made active.  If the flag is a deactivate flag (bit 0 is 0),
  1181. #                then the grow icon is erased and any scroll bars are inactivated.
  1182. #
  1183. #    LIST MANAGER SPECIFICS
  1184. #    ---- ------- ---------
  1185. #    LActivate -- The list manager routine LActivate is called to make a list
  1186. #                either active or inactive.
  1187. }
  1188. PROCEDURE HandleActivateEvt;
  1189.  
  1190. VAR
  1191.     theWindow:        WindowPtr;            { Window that is active/inactive        }
  1192.     windPeek:        DocumentPeek;        { Information about the window            }
  1193.     invH:            Rect;                { Grow icon to be erased                }
  1194.     dActivate:        Boolean;            { True if activate, false if inactive    }
  1195.     
  1196. BEGIN
  1197. theWindow := WindowPtr(gEvents.message);     { Get the window pointer from the event
  1198.                                                record, and coerce it into a window pointer }
  1199. windPeek := DocumentPeek(theWindow);
  1200. dActivate := Odd(gEvents.modifiers);            { Get the modifiers field from the event
  1201.                                                record.    A 1 denotes activation, 0 deactivation }
  1202. if dActivate THEN                            { If it is an activate event, then }
  1203.     BEGIN
  1204.     SetPort(theWindow);                     { Set the current grafPort }
  1205.     gActvWindow := theWindow;                { Make it the active window }
  1206.     LActivate(true, windPeek^.docList);
  1207.     DrawGrowIcon(theWindow);
  1208.     END
  1209. ELSE                                        { If it is a deactivate event, then }
  1210.     BEGIN
  1211.     LActivate(false, windPeek^.docList);
  1212.     SetRect(invH, theWindow^.portRect.right - 14,
  1213.                   theWindow^.portRect.bottom - 14,
  1214.                   theWindow^.portRect.right,
  1215.                   theWindow^.portRect.bottom);
  1216.     EraseRect(invH);
  1217.     END;
  1218. END;
  1219.  
  1220. {
  1221. #    HandleKeyDown -- Respond to a key being pressed.
  1222. #
  1223. #    Called by -- main event loop
  1224. #
  1225. #    Description -- There are several special cases to be checked for this
  1226. #                routine.  If the command key is depressed when the key is down,
  1227. #                then the key down event is a command key equivalent, and the
  1228. #                character that represents the key is passed to the Menu Manager
  1229. #                routine MenuKey.
  1230. #                If the command key is not pressed, then the character should be
  1231. #                passed to the application.  There are several non-printin keys
  1232. #                that need to be handled specially.  These keys are the carrriage
  1233. #                return (13), tab (9), backspace (8), and enter (3).  If the key
  1234. #                pressed wansn't any of these keys, then the character should be
  1235. #                sent to the application.
  1236. #
  1237. #    LIST MANAGER SPECIFICS
  1238. #    ---- ------- ---------
  1239. #    The following special cases are handled in the following way:
  1240. #        CR -- Advance one row in same column
  1241. #        Tab -- Advance one column in the same row.  If the current cell is the
  1242. #            last cell in the row, then go to the first column in the next row.
  1243. #            (For more information on how CR's and tabs are handled, see the
  1244. #            description of LNextCell in the List Manager chapter of Inside 
  1245. #            Macintosh.)
  1246. #        Backspace -- Delete data in the cell
  1247. #        Enter -- Unselect the currently selected cell.
  1248. #        Any printing character -- add the character to the data for the cell.
  1249. }
  1250. PROCEDURE HandleKeyDown(modifiers: INTEGER);
  1251.  
  1252. VAR
  1253.     theKey:         STRING[1];            { The key that was depreseed (char format)    }
  1254.     theCell:        Cell;                { Generic cell variable                        }
  1255.     dataLen:        INTEGER;            { Length of data in cell                    }
  1256.     CP:                Ptr;                { Pointer to data                            }
  1257.     menuInfo:        LONGINT;            { Returned by menu key                        }
  1258.     
  1259. BEGIN
  1260. IF BitAnd(modifiers,cmdKey) = cmdKey THEN    { If the command (apple) key was pressed down }
  1261.     BEGIN                                    { then a menu equivalent was pressed }
  1262.     menuInfo := MenuKey(CHR(BitAnd(gEvents.message,charCodeMask)));
  1263.     HandleMenuSelect(menuInfo);
  1264.     END
  1265. ELSE
  1266.     BEGIN                                    { Otherwise handle the character }
  1267.     SetPt(theCell, 0, 0);
  1268.     IF (LGetSelect(true, theCell, gWindInfo^.docList)) tHEN
  1269.         BEGIN
  1270.         CASE BitAnd(gEvents.message, charCodeMask) OF
  1271.             13: BEGIN    { Carriage return pressed }
  1272.                 LSetSelect(false, theCell, gWindInfo^.docList);
  1273.                 IF (LNextCell(false, true, theCell, gWindInfo^.docList)) THEN
  1274.                     LSetSelect(true, theCell, gWindInfo^.docList)
  1275.                 ELSE
  1276.                     BEGIN
  1277.                     theCell.v := 0;
  1278.                     LSetSelect(true, theCell, gWindInfo^.docList);
  1279.                     END;
  1280.                 END;
  1281.  
  1282.             9:    BEGIN    { Tab pressed }
  1283.                 LSetSelect(false, theCell, gWindInfo^.docList);
  1284.                 IF (LNextCell(true, true, theCell, gWindInfo^.docList)) THEN
  1285.                     LSetSelect(true, theCell, gWindInfo^.docList)
  1286.                 ELSE
  1287.                     BEGIN
  1288.                     theCell.v := 0;
  1289.                     LSetSelect(true, theCell, gWindInfo^.docList);
  1290.                     END;
  1291.                 END;
  1292.             
  1293.             8:    BEGIN    { Backspace pressed }
  1294.                 dataLen := 256;
  1295.                 CP := NewPtr(256);
  1296.                 LGetCell(CP, dataLen, theCell, gWindInfo^.docList);
  1297.                 IF dataLen <> 0 THEN
  1298.                     LSetCell(CP, dataLen-1, theCell, gWindInfo^.docList);
  1299.                 DisposPtr(CP);
  1300.                 END;
  1301.             
  1302.             3:    BEGIN    { Enter pressed }
  1303.                 LSetSelect(false, theCell, gWindInfo^.docList);
  1304.                 END;
  1305.                 
  1306.         OTHERWISE    { It was a printing character }
  1307.             BEGIN
  1308.             theKey[1] := CHR(BitAnd(gEvents.message, charCodeMask));
  1309.             LAddToCell(POINTER(ORD(@theKey)+1), 1, theCell, gWindInfo^.docList);
  1310.             LDraw(theCell, gWindInfo^.docList);
  1311.             END;
  1312.         END;
  1313.         END;
  1314.     END;
  1315. END;
  1316.  
  1317. PROCEDURE _DataInit; EXTERNAL;
  1318.  
  1319. {
  1320. #    The Main Program
  1321. #
  1322. #    Called by -- The entry point
  1323. #
  1324. #    Description -- The main program is used to initialize any variables and
  1325. #                data structures.  After this, the program enters an "event 
  1326. #                loop".  The event loop calls SystemTask, which handles DA's and
  1327. #                other system necessities, then calls GetNextEvent, which returns
  1328. #                an event constant when an event occurs.
  1329. }
  1330. BEGIN {main}
  1331.  
  1332. UnloadSeg(@_DataInit);
  1333. Initialize;                                     { Start initialization }
  1334.  
  1335. repeat                                            { THE START OF THE MAIN LOOP }
  1336. SystemTask;                                     { Handle DA's and VBL tasks }
  1337. IF GetNextEvent(everyEvent,gEvents) THEN         { if there is an event--every event is a mask for
  1338.                                                   all events, as some events may be masked out so
  1339.                                                   they are not recognized. }
  1340.     BEGIN
  1341.     CASE gEvents.what OF
  1342.         mouseDown:    HandleMouseDown;
  1343.                                                 { the mouse was pressed }
  1344.         keyDown:    HandleKeyDown(gEvents.modifiers);
  1345.                                                 { a key was pressed }
  1346.         autoKey:;
  1347.                                                 { a key was pressed and continues to be held down }
  1348.         updateEvt:    HandleUpdateEvt;
  1349.                                                 { an update event was triggered }
  1350.         activateEvt:  HandleActivateEvt;
  1351.                                                 { a window was opened or made active }
  1352.         END; {case}
  1353.     END;
  1354. UNTIL gFinished;  { Th-th-th-that's all folks }
  1355. END.
  1356.